#include "CCImmImpl.h"
#include "platform/win32/CCUtils-win32.h"
#include "GameFunc.h"

NS_CC_BEGIN

std::allocator<char> _alloc;

static std::size_t _calcCharCount(const char * text,size_t max=1)
{
	if (max == 0)
	{
		return 0;
	}
	int n = 0;
	char ch = 0;
	const char * p = text;
	while ((ch = *p))
	{
		CC_BREAK_IF(!ch);

		if (0x80 != (0xC0 & ch))
		{
			++n;
			if (n > max)
			{
				p--;
				break;
			}
		}
		++p;
	}
	return p-text;
}

IMMImpl::IMMImpl()
{
}

IMMImpl::~IMMImpl()
{
}

IMMImpl * IMMImpl::sharedDispatcher()
{
	static IMMImpl s_instance;
	return &s_instance;
}

void IMMImpl::OpenImm()
{
	HWND hwnd = GameFunc::FindWar3Window();
	HIMC himc = ImmAssociateContext(hwnd, *(HIMC*)GameFunc::GetAddress(0xBDAB94));
}

void IMMImpl::CloseImm()
{
	closeCandidate();
	HWND hwnd = GameFunc::FindWar3Window();
	ImmAssociateContext(hwnd, 0);
}

LayerColor * IMMImpl::getLayer(bool isCreate)
{
	if (!_layer && isCreate)
	{
		_layer = LayerColor::create(Color4B(0, 0, 0, 200));
		_layer->retain();
		Scene* scene = Director::getInstance()->getRunningScene();
		scene->addChild(_layer);

		Label* label = Label::createWithSystemFont("W", "", 20);
		label->setTag(1);
		label->setAnchorPoint(Vec2(0, 0));
		_layer->addChild(label);

		LayerColor* separator = LayerColor::create(Color4B(200, 200, 200, 200));
		separator->setTag(2);
		separator->setContentSize(Size(0, 2));
		_layer->addChild(separator);
	}

	return _layer;
}

void IMMImpl::calcCandidateSize()
{
	static float interval = 5;
	Size size, size1;

	LayerColor* layer = getLayer(false);
	if (!layer)
	{
		return;
	}
	for (auto iter : layer->getChildren())
	{
		if (iter->getTag() != 2)
		{
			size1 = iter->getContentSize();
			size.height += size1.height + interval;
			if (size.width < size1.width)
			{
				size.width = size1.width;
			}
		}
		else
		{
			size.height += 2 + interval;
		}
	}
	float height = size.height;
	for (auto iter : layer->getChildren())
	{
		size1 = iter->getContentSize();
		iter->setPosition(10, height - size1.height);
		height -= size1.height + interval;
	}
	size.height += 5;
	size.width += 10;
	LayerColor* separator = dynamic_cast<LayerColor*>(layer->getChildByTag(2));
	separator->setContentSize(Size(size.width, 2));
	Vec2 pos = separator->getPosition();
	pos.x = 0;
	separator->setPosition(pos);

	layer->setContentSize(size);
	layer->setPosition(cocos2d::IMEDispatcher::sharedDispatcher()->getInsertPos());
}

void IMMImpl::showCandidate()
{
	HWND hwnd = GameFunc::FindWar3Window();
	HIMC himc = ImmGetContext(hwnd);
	if (himc)
	{
		DWORD count = ImmGetCandidateListW(himc, _index, 0, 0);
		if (count)
		{
			CANDIDATELIST* list = (CANDIDATELIST*)_alloc.allocate(count);
			ImmGetCandidateListW(himc, _index, list, count);
			if (!list->dwPageSize)
			{
				ImmNotifyIME(himc, NI_SETCANDIDATE_PAGESIZE, _index, 9);
				list->dwPageSize = 9;
			}
			if (list->dwPageSize*(list->dwSelection / list->dwPageSize) != list->dwPageStart)
			{
				ImmNotifyIME(himc, NI_SETCANDIDATE_PAGESTART, _index, list->dwPageSize * (list->dwSelection / list->dwPageSize));
				count = ImmGetCandidateListW(himc, _index, 0, 0);
				if (count)
				{
					_alloc.deallocate((char*)list, list->dwSize);
					list = (CANDIDATELIST*)_alloc.allocate(count);
					ImmGetCandidateListW(himc, _index, list, count);
				}
			}
			if (list->dwPageSize)
			{
				LayerColor* layer = getLayer(true);
				while (Node* node = layer->getChildByTag(3))
				{
					node->removeFromParent();
				}
				
				DWORD v10;
				WCHAR WideCharStr[64];
				for (int i = 0; i < list->dwPageSize; i++)
				{
					v10 = i + list->dwPageStart;
					if (v10 >= list->dwCount)
					{
						break;
					}
					wsprintfW(WideCharStr,L"%d. %s", i+1, (wchar_t*)((char*)list + list->dwOffset[v10]));
					std::string utf8 = StringWideCharToUtf8(WideCharStr);
					Label* label = Label::createWithSystemFont(utf8, "", 20);
					label->setTag(3);
					label->setAnchorPoint(Vec2(0, 0));
					layer->addChild(label);
					if (i == list->dwSelection)
					{
						label->setColor(Color3B(255, 100, 100));

						/*LayerColor* selection = LayerColor::create(Color4B(255, 255, 255, 255));
						Size size = label->getContentSize();
						selection->setContentSize(Size(size.width, 2));
						label->addChild(selection);*/
					}
				}
				calcCandidateSize();
			}
			_alloc.deallocate((char*)list, list->dwSize);

			
		}
		else
		{
			closeCandidate();
		}

		ImmReleaseContext(hwnd, himc);
	}
}

void IMMImpl::closeCandidate()
{
	if (_layer)
	{
		_layer->removeFromParent();
		_layer->release();
		_layer = nullptr;
	}
}

void IMMImpl::compStr(DWORD lParam)
{
	HWND hwnd = GameFunc::FindWar3Window();
	HIMC himc = ImmGetContext(hwnd);
	if (himc)
	{
		if (lParam&GCS_COMPSTR)
		{
			DWORD dwSize = ImmGetCompositionStringW(himc, GCS_COMPSTR, NULL, 0);
			if (dwSize > 0)
			{
				dwSize += sizeof(WCHAR);
				WCHAR* wstr = (WCHAR*)_alloc.allocate(dwSize);
				memset(wstr, 0, dwSize);
				if (wstr)
				{
					ImmGetCompositionStringW(himc, GCS_COMPSTR, wstr, dwSize);
					std::string utf8 = StringWideCharToUtf8(wstr);
					LayerColor* layer = getLayer(true);
					Label* label = dynamic_cast<Label*>(layer->getChildByTag(1));
					label->setString(utf8);
					calcCandidateSize();
					_alloc.deallocate((char*)wstr, dwSize);
				}
			}
		}
		if (lParam&GCS_CURSORPOS)
		{
			LayerColor* layer = getLayer(false);
			if (layer)
			{
				DWORD dwSize = ImmGetCompositionStringW(himc, GCS_CURSORPOS, NULL, 0);
				Label* label = dynamic_cast<Label*>(layer->getChildByTag(1));
				LayerColor* cursor = dynamic_cast<LayerColor*>(label->getChildByTag(1));
				if (!cursor)
				{
					cursor = LayerColor::create(Color4B(255, 255, 255, 255));
					cursor->setTag(1);
					Size size = label->getContentSize();
					cursor->setContentSize(Size(2, size.height));
					label->addChild(cursor);
				}
				Size size;
				if (dwSize)
				{
					const std::string& str = label->getString();
					std::string buffer;
					size_t len = _calcCharCount(str.c_str(), dwSize);
					buffer.assign(str.c_str(), len);

					size = Device::getSizeForText(buffer.c_str(), label->getFontDefinition(), Device::TextAlign::LEFT);
				}
				cursor->setPosition(Vec2(size.width, 0));
			}
		}
		ImmReleaseContext(hwnd, himc);
	}
}

void IMMImpl::resultStr()
{
	HWND hwnd = GameFunc::FindWar3Window();
	HIMC himc = ImmGetContext(hwnd);
	if (himc)
	{
		DWORD dwSize = ImmGetCompositionStringW(himc, GCS_RESULTSTR, NULL, 0);
		if (dwSize > 0)
		{
			dwSize += sizeof(WCHAR);
			WCHAR* wstr = (WCHAR*)_alloc.allocate(dwSize);
			memset(wstr, 0, dwSize);
			if (wstr)
			{
				ImmGetCompositionStringW(himc, GCS_RESULTSTR, wstr, dwSize);
				std::string utf8 = StringWideCharToUtf8(wstr);
				cocos2d::IMEDispatcher::sharedDispatcher()->dispatchInsertText(utf8.c_str(), utf8.length());
				_alloc.deallocate((char*)wstr, dwSize);
			}
		}
		ImmReleaseContext(hwnd, himc);
	}
}

int IMMImpl::ImmMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if (cocos2d::Director::getInstance()->isPaused())
	{
		return -1;
	}
	if (!cocos2d::IMEDispatcher::sharedDispatcher()->isAnyDelegateAttachedWithIME())
	{
		return -1;
	}

	switch (uMsg)
	{
	case WM_IME_STARTCOMPOSITION:
	{
		//DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
		break;
	case WM_IME_COMPOSITION:
	{
		if (lParam&(GCS_CURSORPOS| GCS_COMPSTR))
		{
			compStr(lParam);
		}
		if (lParam&GCS_RESULTSTR)
		{
			resultStr();
		}
		//DefWindowProc(hWnd, uMsg, wParam, lParam);
		break;
	}
	case WM_IME_SETCONTEXT: //激活窗口时发送到应用程序 wParam 如果窗口处于活动状态，则为 TRUE;否则为 FALSE
		if (wParam)
		{
			lParam = lParam ^ (ISC_SHOWUICOMPOSITIONWINDOW);
		}
		return DefWindowProc( hWnd,  uMsg,  wParam, lParam);
		break;
	//case WM_IME_CONTROL: //应用程序发送以指示 IME 窗口执行请求的命令
	//	break;
	//case WM_IME_ENDCOMPOSITION: //当 IME 结束组合时发送到应用程序,使用ImmGetCompositionString获得最终生成的中文字符
	//	break;
	case WM_IME_NOTIFY:
		switch (wParam)
		{
		case IMN_CHANGECANDIDATE:
			showCandidate();
			break;
		case IMN_CLOSECANDIDATE:
		case IMN_CLOSESTATUSWINDOW:
			closeCandidate();
			break;
		}
		//DefWindowProc(hWnd, uMsg, wParam, lParam);
		break;
	//case WM_IME_REQUEST:
	//	switch (wParam)
	//	{
	//	case IMR_CANDIDATEWINDOW:
	//		break;
	//	case IMR_COMPOSITIONWINDOW:
	//		break;
	//	default:
	//		break;
	//	}
	//	break;
	//case WM_IME_CHAR:
	//	printf("");
	//	break;
	default:
	{
		//DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
		break;
	}
	return 0;
}

NS_CC_END